import { DatabaseService } from '../../database/database.service'
import { LoggerUtil, LogContext } from './logger.util'
import { ExceptionUtil } from './exception.util'
import { Prisma } from '@prisma/client'
import { Logger } from '@nestjs/common'

/**
 * 分页查询选项
 */
export interface PaginationOptions {
  page?: number
  limit?: number
  sortBy?: string
  sortOrder?: 'asc' | 'desc'
}

/**
 * 分页响应结果
 */
export interface PaginatedResult<T> {
  data: T[]
  total: number
  page: number
  limit: number
  totalPages: number
}

/**
 * 搜索选项
 */
export interface SearchOptions {
  fields: string[]
  query: string
  mode?: 'insensitive' | 'default'
}

/**
 * 查询构建器选项
 */
export interface QueryBuilderOptions {
  where?: any
  include?: any
  select?: any
  orderBy?: any
}

/**
 * 统计查询选项
 */
export interface StatsOptions {
  groupBy?: string[]
  where?: any
  orderBy?: any
}

/**
 * 数据库查询工具类
 * 提供通用的数据库查询方法，减少重复代码
 */
export class DatabaseUtil {
  private static readonly logger = new Logger(DatabaseUtil.name)
  /**
   * 执行分页查询
   * @param model Prisma模型
   * @param options 分页选项
   * @param queryOptions 查询选项
   * @param context 日志上下文
   * @returns 分页结果
   */
  static async paginate<T>(
    model: any,
    options: PaginationOptions = {},
    queryOptions: QueryBuilderOptions = {},
    context?: LogContext
  ): Promise<PaginatedResult<T>> {
    try {
      const { page = 1, limit = 10, sortBy = 'createdAt', sortOrder = 'desc' } = options

      const skip = (page - 1) * limit
      const orderBy = queryOptions.orderBy || { [sortBy]: sortOrder }

      const [data, total] = await Promise.all([
        model.findMany({
          ...queryOptions,
          skip,
          take: limit,
          orderBy
        }),
        model.count({ where: queryOptions.where })
      ])

      const result = {
        data,
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit)
      }

      LoggerUtil.dbOperation(DatabaseUtil.logger, '分页查询', model.name || 'unknown', undefined, context)

      return result
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '分页查询失败', model.name || 'unknown', undefined, context)
      throw ExceptionUtil.handleDatabaseError(error, '分页查询失败')
    }
  }

  /**
   * 构建搜索条件
   * @param searchOptions 搜索选项
   * @returns Prisma where条件
   */
  static buildSearchCondition(searchOptions: SearchOptions): any {
    const { fields, query, mode = 'insensitive' } = searchOptions

    if (!query || !fields.length) {
      return {}
    }

    return {
      OR: fields.map(field => ({
        [field]: {
          contains: query,
          mode
        }
      }))
    }
  }

  /**
   * 构建过滤条件
   * @param filters 过滤器对象
   * @param fieldMappings 字段映射
   * @returns Prisma where条件
   */
  static buildFilterCondition(
    filters: Record<string, any>,
    fieldMappings: Record<string, string | ((value: any) => any)> = {}
  ): any {
    const where: any = {}

    Object.entries(filters).forEach(([key, value]) => {
      if (value === undefined || value === null || value === '') {
        return
      }

      const mapping = fieldMappings[key]
      if (typeof mapping === 'function') {
        Object.assign(where, mapping(value))
      } else {
        const fieldName = mapping || key
        where[fieldName] = value
      }
    })

    return where
  }

  /**
   * 执行唯一性检查
   * @param model Prisma模型
   * @param field 字段名
   * @param value 字段值
   * @param excludeId 排除的ID（用于更新时检查）
   * @param context 日志上下文
   * @returns 是否唯一
   */
  static async checkUniqueness(
    model: any,
    field: string,
    value: any,
    excludeId?: string,
    context?: LogContext
  ): Promise<boolean> {
    try {
      const where: any = { [field]: value }
      if (excludeId) {
        where.id = { not: excludeId }
      }

      const existing = await model.findFirst({ where })
      const isUnique = !existing

      LoggerUtil.dbOperation(DatabaseUtil.logger, '唯一性检查', model.name || 'unknown', field, context)

      return isUnique
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '唯一性检查失败', model.name || 'unknown', field, context)
      throw ExceptionUtil.handleDatabaseError(error, '唯一性检查失败')
    }
  }

  /**
   * 执行批量操作
   * @param operations 操作数组
   * @param context 日志上下文
   * @returns 操作结果
   */
  static async batchOperation(operations: (() => Promise<any>)[], context?: LogContext): Promise<any[]> {
    try {
      const results = await Promise.all(operations)

      LoggerUtil.dbOperation(DatabaseUtil.logger, '批量操作', 'batch', operations.length.toString(), context)

      return results
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '批量操作失败', 'batch', operations.length.toString(), context)
      throw ExceptionUtil.handleDatabaseError(error, '批量操作失败')
    }
  }

  /**
   * 执行事务操作
   * @param databaseService 数据库服务
   * @param operations 事务操作函数
   * @param context 日志上下文
   * @returns 事务结果
   */
  static async transaction<T>(
    databaseService: DatabaseService,
    operations: (tx: Prisma.TransactionClient) => Promise<T>,
    context?: LogContext
  ): Promise<T> {
    try {
      const result = await databaseService.$transaction(operations)

      LoggerUtil.dbOperation(DatabaseUtil.logger, '事务操作', 'transaction', undefined, context)

      return result
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '事务操作失败', 'transaction', undefined, context)
      throw ExceptionUtil.handleDatabaseError(error, '事务操作失败')
    }
  }

  /**
   * 获取统计信息
   * @param model Prisma模型
   * @param options 统计选项
   * @param context 日志上下文
   * @returns 统计结果
   */
  static async getStats(model: any, options: StatsOptions = {}, context?: LogContext): Promise<any> {
    try {
      const { groupBy, where, orderBy } = options

      if (groupBy) {
        const result = await model.groupBy({
          by: groupBy,
          where,
          _count: true,
          orderBy
        })

        LoggerUtil.dbOperation(DatabaseUtil.logger, '分组统计', model.name || 'unknown', undefined, context)

        return result
      } else {
        const count = await model.count({ where })

        LoggerUtil.dbOperation(DatabaseUtil.logger, '计数统计', model.name || 'unknown', undefined, context)

        return { count }
      }
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '统计查询失败', model.name || 'unknown', undefined, context)
      throw ExceptionUtil.handleDatabaseError(error, '统计查询失败')
    }
  }

  /**
   * 软删除记录
   * @param model Prisma模型
   * @param id 记录ID
   * @param context 日志上下文
   * @returns 更新结果
   */
  static async softDelete(model: any, id: string, context?: LogContext): Promise<any> {
    try {
      const result = await model.update({
        where: { id },
        data: {
          deletedAt: new Date(),
          isActive: false
        }
      })

      LoggerUtil.dbOperation(DatabaseUtil.logger, '软删除', model.name || 'unknown', id, context)

      return result
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '软删除失败', model.name || 'unknown', id, context)
      throw ExceptionUtil.handleDatabaseError(error, '软删除失败')
    }
  }

  /**
   * 恢复软删除的记录
   * @param model Prisma模型
   * @param id 记录ID
   * @param context 日志上下文
   * @returns 更新结果
   */
  static async restore(model: any, id: string, context?: LogContext): Promise<any> {
    try {
      const result = await model.update({
        where: { id },
        data: {
          deletedAt: null,
          isActive: true
        }
      })

      LoggerUtil.dbOperation(DatabaseUtil.logger, '恢复记录', model.name || 'unknown', id, context)

      return result
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '恢复记录失败', model.name || 'unknown', id, context)
      throw ExceptionUtil.handleDatabaseError(error, '恢复记录失败')
    }
  }

  /**
   * 构建包含关系
   * @param includes 包含配置
   * @returns Prisma include对象
   */
  static buildInclude(includes: string[] | Record<string, any>): any {
    if (Array.isArray(includes)) {
      const result: Record<string, any> = {}
      includes.forEach(field => {
        result[field] = true
      })
      return result
    }
    return includes
  }

  /**
   * 构建选择字段
   * @param fields 字段数组或对象
   * @returns Prisma select对象
   */
  static buildSelect(fields: string[] | Record<string, any>): any {
    if (Array.isArray(fields)) {
      const result: Record<string, any> = {}
      fields.forEach(field => {
        result[field] = true
      })
      return result
    }
    return fields
  }

  /**
   * 验证记录存在性
   * @param model Prisma模型
   * @param id 记录ID
   * @param context 日志上下文
   * @returns 记录是否存在
   */
  static async exists(model: any, id: string, context?: LogContext): Promise<boolean> {
    try {
      const record = await model.findUnique({
        where: { id },
        select: { id: true }
      })

      const exists = !!record

      LoggerUtil.dbOperation(DatabaseUtil.logger, '存在性检查', model.name || 'unknown', id, context)

      return exists
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '存在性检查失败', model.name || 'unknown', id, context)
      throw ExceptionUtil.handleDatabaseError(error, '存在性检查失败')
    }
  }

  /**
   * 获取记录详情（带缓存支持）
   * @param model Prisma模型
   * @param id 记录ID
   * @param options 查询选项
   * @param context 日志上下文
   * @returns 记录详情
   */
  static async findById<T>(
    model: any,
    id: string,
    options: QueryBuilderOptions = {},
    context?: LogContext
  ): Promise<T | null> {
    try {
      const record = await model.findUnique({
        where: { id },
        ...options
      })

      LoggerUtil.dbOperation(DatabaseUtil.logger, '按ID查询', model.name || 'unknown', id, context)

      return record
    } catch (error) {
      LoggerUtil.dbOperation(DatabaseUtil.logger, '按ID查询失败', model.name || 'unknown', id, context)
      throw ExceptionUtil.handleDatabaseError(error, '按ID查询失败')
    }
  }
}